#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright Mark Daniel (markdaniel@avolites.com)
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License

import inkex
import gettext
import os
import sys
import tempfile
import re

_ = gettext.gettext

# Built in options
NativeResolution = 160
Export = {"create-ldpi" : {"dir" : "drawable-ldpi", "res" : 120, "suffix" : ""},
          "create-mdpi" : {"dir" : "drawable-mdpi", "res" : 160, "suffix" : ""}, 
          "create-genericdpi" : {"dir" : "drawable", "res" : 160, "suffix" : ""},
          "create-base" : {"dir" : "", "res" : 160, "suffix" : ""},
          "create-base2x" : {"dir" : "", "res" : 320, "suffix" : "@2x"},
          "create-hdpi" : {"dir" : "drawable-hdpi", "res" : 240, "suffix" : ""},
          "create-xhdpi" : {"dir" : "drawable-xhdpi", "res" : 320, "suffix" : ""} }



def is_empty(val):
    if val is None:
        return True
    else:
        return len(str(val)) == 0


class ExportPngsEffect(inkex.Effect):

    def __init__(self):
        inkex.Effect.__init__(self)
        self.OptionParser.add_option("--dir",
                                     action="store", type="string",
                                     dest="dir",
                                     help="")
        self.OptionParser.add_option("--create-dir",
                                     action="store", type="inkbool",
                                     default=False,
                                     dest="create_dir",
                                     help="")
        self.OptionParser.add_option("--width",
                                     action="store", type=int,
                                     default=100,
                                     dest="width",
                                     help="Sets the output width at mDPI")
        self.OptionParser.add_option("--leafname",
                                     action="store", type="string",
                                     default="image.png",
                                     dest="leafname",
                                     help="The output filename")
        self.OptionParser.add_option("--create-genericdpi",
                                     action="store", type="inkbool",
                                     default=True,
                                     dest="create_genericdpi",
                                     help="")
        self.OptionParser.add_option("--create-ldpi",
                                     action="store", type="inkbool",
                                     default=False,
                                     dest="create_ldpi",
                                     help="")
        self.OptionParser.add_option("--create-mdpi",
                                     action="store", type="inkbool",
                                     default=False,
                                     dest="create_mdpi",
                                     help="")
        self.OptionParser.add_option("--create-hdpi",
                                     action="store", type="inkbool",
                                     default=True,
                                     dest="create_hdpi",
                                     help="")
        self.OptionParser.add_option("--create-xhdpi",
                                     action="store", type="inkbool",
                                     default=True,
                                     dest="create_xhdpi",
                                     help="")
        self.OptionParser.add_option("--create-ninepatch",
                                     action="store", type="inkbool",
                                     default=False,
                                     dest="create_ninepatch",
                                     help="")
        self.OptionParser.add_option("--create-base",
                                    action="store", type="inkbool",
                                    default=False,
                                    dest="create_base",
                                    help="")
        self.OptionParser.add_option("--create-base-2x",
                                     action="store", type="inkbool",
                                     default=False,
                                     dest="create_2x",
                                     help="")
        self.OptionParser.add_option("--ignore")
    
    #self.OptionParser.add_option('-o', '--output', help='The output filename and path', required=True)
        #self.OptionParser.add_option('input', help='The input SVG filename')

    svgNS = '{http://www.w3.org/2000/svg}'

    selectedExports = []

    def validate_inputs(self):
        # The user must supply a directory to export:
        if is_empty( self.options.dir ):
            inkex.errormsg(_('You must to give a directory to export the drawables.'))
            return {'error':'You must to give a directory to export the drawables.'}
        # No directory separator at the path end:
        if self.options.dir[-1] == '/' or self.options.dir[-1] == '\\':
            self.options.dir = self.options.dir[0:-1]
        # Test if the directory exists:
        if not os.path.exists( self.options.dir ):
            if self.options.create_dir:
                # Try to create it:
                try:
                    os.makedirs( self.options.dir )
                except Exception, e:
                    inkex.errormsg( _('Can\'t create "%s".') % self.options.dir )
                    inkex.errormsg( _('Error: %s') % e )
                    return {'error':'Can\'t create the directory to export.'}
            else:
                inkex.errormsg(_('The directory "%s" does not exists.') % self.options.dir)
                return

        if self.options.create_ldpi:
            self.selectedExports.append(Export["create-ldpi"])

        if self.options.create_mdpi:
            self.selectedExports.append(Export["create-mdpi"])

        if self.options.create_hdpi:
            self.selectedExports.append(Export["create-hdpi"])

        if self.options.create_xhdpi:
            self.selectedExports.append(Export["create-xhdpi"])

        if self.options.create_genericdpi:
            self.selectedExports.append(Export["create-genericdpi"])

        if self.options.create_base:
            self.selectedExports.append(Export["create-base"])

        if self.options.create_2x:
            self.selectedExports.append(Export["create-base2x"])
                        
        magic = self.test_if_has_imagemagick()

        if not self.has_magick and self.options.create_ninepatch:
            inkex.errormsg(_('You must install ImageMagick to export 9 patch. %s') % magic)
            self.options.create_ninepatch = False

        return None

    
    def get_cmd_output(self, cmd):
        # This solution comes from Andrew Reedick <jr9445 at ATT.COM>
        # http://mail.python.org/pipermail/python-win32/2008-January/006606.html
        # This method replaces the commands.getstatusoutput() usage, with the
        # hope to correct the windows exporting bug:
        # https://bugs.launchpad.net/inkscape/+bug/563722
        if sys.platform != "win32": cmd = '{ '+ cmd +'; }'
        pipe = os.popen(cmd +' 2>&1', 'r')
        text = pipe.read()
        sts = pipe.close()
        if sts is None: sts = 0
        if text[-1:] == '\n': text = text[:-1]
        return sts, text

    def get_cmd_output_dumperr(self, cmd):
        # This solution comes from Andrew Reedick <jr9445 at ATT.COM>
        # http://mail.python.org/pipermail/python-win32/2008-January/006606.html
        # This method replaces the commands.getstatusoutput() usage, with the
        # hope to correct the windows exporting bug:
        # https://bugs.launchpad.net/inkscape/+bug/563722
        if sys.platform != "win32": cmd = '{ '+ cmd +'; }'
        pipe = os.popen(cmd +( ' 2>NUL' if sys.platform == "win32" else ' 2>/dev/null'), 'r')
        text = pipe.read()
        sts = pipe.close()
        if sts is None: sts = 0
        if text[-1:] == '\n': text = text[:-1]
        return sts, text

    def test_if_has_imagemagick(self):
        (status, output) = self.get_cmd_output('convert --version')
        self.has_magick = ( status == 0 and 'ImageMagick' in output )
        return output

    def effect(self):
        error = self.validate_inputs()
        if error: return error

        self.create_the_temporary_svg()

        error = self.create_images()
        if error: return error

        self.delete_the_temporary_svg()

    def create_images(self):

     
        for output in self.selectedExports:

            if output["dir"]:
                resPath = os.path.join(self.options.dir, output["dir"])
            else:
                resPath = self.options.dir;
            
            if not os.path.exists(resPath):
                try:
                    os.makedirs( resPath )
                except Exception, e:
                    inkex.errormsg( _('Can\'t create "%s".') % resPath )
                    inkex.errormsg( _('Error: %s') % e )
                    return {'error':'Can\'t create the directory to export.'}

            outputWidth = self.options.width * output["res"] / NativeResolution
            
            if self.options.create_ninepatch:
                (pageImageRef, pageImagePath) = tempfile.mkstemp('.png')
            else:
                if output["suffix"]:
                    pageImagePath = os.path.join(resPath, self.options.leafname.replace(".png", output["suffix"]+".png"))
                else:
                    pageImagePath = os.path.join(resPath, self.options.leafname)
                pageImageRef = 0

            # Create the centre part of the image
            inkscapeCommand =  "inkscape --file=\"" + self.tmp_svg + "\"" + \
               " --export-png=\"" + pageImagePath + "\"" + \
               " --export-area-page" + \
               " --export-width=" + str(outputWidth) + \
               " --without-gui " 

            (status, cmdOutput) = self.get_cmd_output(inkscapeCommand)

            if self.options.create_ninepatch:
                # Create the full image
                (fullImageRef, fullImagePath) = tempfile.mkstemp('-full.png')

                # Get the dimensions of the document (ie the page) in inkscape units
                self.doc_width = inkex.unittouu(self.document.getroot().get('width'))
                self.doc_height = inkex.unittouu(self.document.getroot().get('height'))

                # Get the dimensions and origin of the drawing (including all the stuff outside the page boundary)
                inkscapeCommand =  "inkscape --file=\"" + self.tmp_svg + "\" -X --without-gui "
                (status, cmdOutput) = self.get_cmd_output_dumperr(inkscapeCommand)
                self.drawing_x = float(cmdOutput)
                inkscapeCommand =  "inkscape --file=\"" + self.tmp_svg + "\" -Y --without-gui "
                (status, cmdOutput) = self.get_cmd_output_dumperr(inkscapeCommand)
                self.drawing_y = float(cmdOutput)
                inkscapeCommand =  "inkscape --file=\"" + self.tmp_svg + "\" -W --without-gui "
                (status, cmdOutput) = self.get_cmd_output_dumperr(inkscapeCommand)
                self.drawing_width = float(cmdOutput)
                inkscapeCommand =  "inkscape --file=\"" + self.tmp_svg + "\" -H --without-gui "
                (status, cmdOutput) = self.get_cmd_output_dumperr(inkscapeCommand)
                self.drawing_height = float(cmdOutput)

                fullImageWidth = int(float(outputWidth) * self.drawing_width / self.doc_width)
                outputHeight = int(float(self.doc_height) / float(self.doc_width) * float(outputWidth))

                outputXOffset = (min(0, self.drawing_x) * - 1) * outputWidth / self.doc_width
                outputYOffset = (min(0, self.drawing_y) * - 1) * outputHeight / self.doc_height

                inkscapeCommand =  "inkscape --file=\"" + self.tmp_svg + "\"" + \
                    " --export-png=\"" + fullImagePath + "\"" + \
                    " --export-area-drawing" + \
                    " --export-width=" + str(fullImageWidth) + \
                    " --without-gui "

                (status, cmdOutput) = self.get_cmd_output(inkscapeCommand)
                if status != 0:
                    inkex.errormsg( _('Can\'t export full image') )
                    inkex.errormsg( _('Error: %s') % output )
                    return {'error':'Can\'t create the full image.'}

                # Convert the full image (including all the borders) to one that only has
                # black and transparent pixels
                (bwImageRef, bwImagePath) = tempfile.mkstemp('-bw.png')

                # This conversion thresholds the alpha channel and sets all visible pixels to black
                # It seems to work on Linux and Windows versions of ImageMagick which -monochrome doesn't.
                (status, cmdOutput) = self.get_cmd_output(
                'convert "%s" -channel Alpha -threshold 50%% -channel RGB -evaluate set 0 "%s"' % ( fullImagePath, bwImagePath )
                )
                if status != 0:
                    inkex.errormsg('Upss... ImageMagick error: '+cmdOutput)

                # Strip the outer edges from that image
                (topImageRef, topImagePath) = tempfile.mkstemp('-top.png')
                (status, cmdOutput) = self.get_cmd_output(
                'convert "%s" -crop %dx%d+%d+%d +repage "%s"' % ( bwImagePath, outputWidth, 1, outputXOffset, 0, topImagePath )
                )
                if status != 0:
                    inkex.errormsg('Upss... ImageMagick error: '+cmdOutput)

                (bottomImageRef, bottomImagePath) = tempfile.mkstemp('-bottom.png')
                (status, cmdOutput) = self.get_cmd_output(
                'convert "%s" -gravity SouthWest -crop %dx%d+%d+%d +repage "%s"' % ( bwImagePath, outputWidth, 1, outputXOffset, 0, bottomImagePath )
                )
                if status != 0:
                    inkex.errormsg('Upss... ImageMagick error: '+cmdOutput)

                (leftImageRef, leftImagePath) = tempfile.mkstemp('-left.png')
                (status, cmdOutput) = self.get_cmd_output(
                'convert "%s" -gravity NorthWest -crop %dx%d+%d+%d +repage "%s"' % ( bwImagePath, 1, outputHeight, 0, outputYOffset, leftImagePath )
                )
                if status != 0:
                    inkex.errormsg('Upss... ImageMagick error: '+cmdOutput)
                
                (rightImageRef, rightImagePath) = tempfile.mkstemp('-right.png')
                (status, cmdOutput) = self.get_cmd_output(
                'convert "%s" -gravity NorthEast -crop %dx%d+%d+%d +repage "%s"' % ( bwImagePath, 1, outputHeight, 0, outputYOffset, rightImagePath)
                )
                if status != 0:
                    inkex.errormsg('Upss... ImageMagick error: '+cmdOutput)

                # Glue those black and transparent borders back on to the full colour page crop
                ninePatchImagePath = os.path.join(resPath, self.options.leafname).replace(".png", ".9.png")
                (status, cmdOutput) = self.get_cmd_output(
                  'convert -background None "%s" ''('' "%s" "%s" "%s" +append '')'' "%s" -gravity North -append -define png:color-type=''6'' "%s"' % ( topImagePath,  leftImagePath, pageImagePath, rightImagePath, bottomImagePath,  ninePatchImagePath )
                  if sys.platform == "win32" else 
                  'convert -background None "%s" \'(\' "%s" "%s" "%s" +append \')\' "%s" -gravity North -append -define png:color-type=\'6\' "%s"' % ( topImagePath, leftImagePath,  pageImagePath, rightImagePath, bottomImagePath, ninePatchImagePath )
                )
                if status != 0:
                    inkex.errormsg('Upss... ImageMagick error: '+cmdOutput)

                # Clear away all the temp files
                os.close(fullImageRef)
                os.remove(fullImagePath)

                os.close(bwImageRef)
                os.remove(bwImagePath)

                os.close(topImageRef)
                os.remove(topImagePath)

                os.close(bottomImageRef)
                os.remove(bottomImagePath)

                os.close(leftImageRef)
                os.remove(leftImagePath)

                os.close(rightImageRef)
                os.remove(rightImagePath)

                os.close(pageImageRef)
                os.remove(pageImagePath)
        
        return None

    def create_the_temporary_svg(self):
        (ref, self.tmp_svg) = tempfile.mkstemp('.svg')
        self.document.write( self.tmp_svg );
        os.close(ref)

        
    def delete_the_temporary_svg(self):
        os.remove( self.tmp_svg )

    
    def output(self):
        # Cancel document serialization to stdout
        pass

if __name__ == '__main__':
    e = ExportPngsEffect()
    e.affect()

